﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace CrazyDrawShape
{
    /// <summary>
    /// DrawEllipseControl.xaml 的交互逻辑
    /// </summary>
    public partial class DrawEllipseControl : UserControl
    {
        private enum DrawType
        {
            Draw,
            Edit
        }
        private enum EditType
        {
            Left,
            Top,
            Right,
            Bottom,
            LeftTop,
            RightTop,
            LeftBottom,
            RightBottom,
            Rotate,
            Internal,
            None
        }
        Path box = new Path { Stroke = Brushes.Red, StrokeThickness = 2, Opacity = 0.5, ClipToBounds = true };
        Path ellipse = new Path { Stroke = Brushes.Red, StrokeThickness = 2, Fill = Brushes.AliceBlue, Opacity = 0.5, ClipToBounds = true };
        Path rotate = new Path { Stroke = Brushes.Red, StrokeThickness = 1, Fill = Brushes.Red, ClipToBounds = true };
        bool isRun = false;
        bool isDown = false;
        Point downPoint;
        DrawType drawType = DrawType.Draw;
        EditType editType = EditType.None;

        public DrawEllipseControl()
        {
            InitializeComponent();
            this.ellipse.MouseEnter += EllipseMouseEnter;
            this.ellipse.MouseLeave += EllipseMouseLeave;
            this.ellipse.MouseDown += GridMouseDown;
            this.ellipse.MouseMove += GridMouseMove;
            this.ellipse.MouseUp += GridMouseUp;
        }

        private void EllipseMouseEnter(object sender, MouseEventArgs e)
        {
            this.isRun = true;
        }

        private void EllipseMouseLeave(object sender, MouseEventArgs e)
        {
            this.isRun = false;
        }

        private void DrawClick(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.Draw;
        }

        private void EditClick(object sender, RoutedEventArgs e)
        {
            this.drawType = DrawType.Edit;
        }

        private double PPDistance(Point p1, Point p2)
        {
            return Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
        }

        private EditType GetEditType(Point point)
        {
            RectangleGeometry rect = (RectangleGeometry)this.box.Data;
            Matrix matrix = rect.Transform.Value;
            matrix.Invert();
            EllipseGeometry circle = (EllipseGeometry)this.rotate.Data;
            double distance = 5;
            Point newPoint = point * matrix;
            if (PPDistance(newPoint, rect.Rect.TopLeft) < distance)
            {
                return EditType.LeftTop;
            }
            else if (PPDistance(newPoint, rect.Rect.TopRight) < distance)
            {
                return EditType.RightTop;
            }
            else if (PPDistance(newPoint, rect.Rect.BottomLeft) < distance)
            {
                return EditType.LeftBottom;
            }
            else if (PPDistance(newPoint, rect.Rect.BottomRight) < distance)
            {
                return EditType.RightBottom;
            }
            else if (PPDistance(point, circle.Center) < distance)
            {
                return EditType.Rotate;
            }
            else if (newPoint.X > rect.Rect.Left - distance && newPoint.X < rect.Rect.Left + distance && newPoint.Y > rect.Rect.Top && newPoint.Y < rect.Rect.Bottom)
            {
                return EditType.Left;
            }
            else if (newPoint.X > rect.Rect.Right - distance && newPoint.X < rect.Rect.Right + distance && newPoint.Y > rect.Rect.Top && newPoint.Y < rect.Rect.Bottom)
            {
                return EditType.Right;
            }
            else if (newPoint.Y > rect.Rect.Top - distance && newPoint.Y < rect.Rect.Top + distance && newPoint.X > rect.Rect.Left && newPoint.X < rect.Rect.Right)
            {
                return EditType.Top;
            }
            else if (newPoint.Y > rect.Rect.Bottom - distance && newPoint.Y < rect.Rect.Bottom + distance && newPoint.X > rect.Rect.Left && newPoint.X < rect.Rect.Right)
            {
                return EditType.Bottom;
            }
            else if (this.isRun)
            {
                return EditType.Internal;
            }
            else
            {
                return EditType.None;
            }
        }

        private void GridMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                this.downPoint = e.GetPosition(mainGrid);
                this.isDown = true;
                switch (this.drawType)
                {
                    case DrawType.Draw:
                        mainGrid.Children.Clear();
                        mainGrid.Children.Add(this.ellipse);
                        mainGrid.Children.Add(this.box);
                        mainGrid.Children.Add(this.rotate);
                        this.box.Data = null;
                        this.ellipse.Data = null;
                        this.rotate.Data = null;
                        break;
                    case DrawType.Edit:
                        if (this.box.Data != null)
                        {
                            this.editType = GetEditType(this.downPoint);
                        }
                        else
                        {
                            this.editType = EditType.None;
                        }
                        break;
                }
            }
        }

        private void UpdateEllipse()
        {
            if (this.ellipse.Data != null)
            {
                RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                EllipseGeometry ellipse = new EllipseGeometry
                {
                    Center = new Point((rectangle.Rect.TopLeft.X + rectangle.Rect.BottomRight.X) / 2, (rectangle.Rect.TopLeft.Y + rectangle.Rect.BottomRight.Y) / 2),
                    RadiusX = PPDistance(rectangle.Rect.TopLeft, rectangle.Rect.TopRight) / 2,
                    RadiusY = PPDistance(rectangle.Rect.TopLeft, rectangle.Rect.BottomLeft) / 2,
                    Transform = rectangle.Transform
                };
                this.ellipse.Data = ellipse;
            }
        }

        private void GridMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (this.drawType == DrawType.Edit)
            {
                switch (this.editType)
                {
                    case EditType.Left:
                    case EditType.Top:
                    case EditType.Right:
                    case EditType.Bottom:
                    case EditType.LeftTop:
                    case EditType.RightTop:
                    case EditType.LeftBottom:
                    case EditType.RightBottom:
                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                        Point topLeft = rectangle.Rect.TopLeft;
                        Point bottomRight = rectangle.Rect.BottomRight;
                        Matrix matrix = rectangle.Transform.Value;
                        Point rotateTopLeft = topLeft * matrix;
                        Point rotateBottomRight = bottomRight * matrix;
                        Point center = new Point((rotateTopLeft.X + rotateBottomRight.X) / 2, (rotateTopLeft.Y + rotateBottomRight.Y) / 2);
                        RotateTransform transform = new RotateTransform(((RotateTransform)rectangle.Transform).Angle, center.X, center.Y);
                        Point newTopLeft = transform.Inverse.Transform(rotateTopLeft);
                        Point newBottomRight = transform.Inverse.Transform(rotateBottomRight);
                        rectangle.Transform = transform;
                        rectangle.Rect = new Rect(newTopLeft, newBottomRight);
                        UpdateEllipse();
                        break;
                }
            }
            this.isDown = false;
            this.editType = EditType.None;
        }

        private void GridMouseMove(object sender, MouseEventArgs e)
        {
            if (this.isDown)
            {
                switch (this.drawType)
                {
                    case DrawType.Draw:
                        {
                            Point curPoint = e.GetPosition(mainGrid);
                            Point center = new Point((this.downPoint.X + curPoint.X) / 2, (this.downPoint.Y + curPoint.Y) / 2);
                            RectangleGeometry rectangle = new RectangleGeometry
                            {
                                Rect = new Rect(this.downPoint, e.GetPosition(mainGrid)),
                                Transform = new RotateTransform(0, center.X, center.Y)
                            };
                            this.box.Data = rectangle;
                            EllipseGeometry ellipse = new EllipseGeometry
                            {
                                Center = new Point(center.X, center.Y),
                                RadiusX = PPDistance(rectangle.Rect.TopLeft, rectangle.Rect.TopRight) / 2,
                                RadiusY = PPDistance(rectangle.Rect.TopLeft, rectangle.Rect.BottomLeft) / 2,
                                Transform = new RotateTransform(0, center.X, center.Y)
                            };
                            this.ellipse.Data = ellipse;
                            this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((rectangle.Rect.TopLeft.X + rectangle.Rect.TopRight.X) / 2, (rectangle.Rect.TopLeft.Y + rectangle.Rect.TopRight.Y) / 2) };
                        }
                        break;
                    case DrawType.Edit:
                        {
                            Point curPoint = e.GetPosition(mainGrid);
                            RotateTransform rotate = new RotateTransform();
                            switch (this.editType)
                            {
                                case EditType.Left:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(new Point(newCurPoint.X, rectangle.Rect.Top), rectangle.Rect.BottomRight);
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.Top:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(new Point(rectangle.Rect.Left, newCurPoint.Y), rectangle.Rect.BottomRight);
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.Right:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(rectangle.Rect.TopLeft, new Point(newCurPoint.X, rectangle.Rect.Bottom));
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.Bottom:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(rectangle.Rect.TopLeft, new Point(rectangle.Rect.Right, newCurPoint.Y));
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.LeftTop:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(newCurPoint, rectangle.Rect.BottomRight);
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.RightTop:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(new Point(rectangle.Rect.Left, newCurPoint.Y), new Point(newCurPoint.X, rectangle.Rect.Bottom));
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.LeftBottom:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(new Point(newCurPoint.X, rectangle.Rect.Top), new Point(rectangle.Rect.Right, newCurPoint.Y));
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.RightBottom:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Matrix matrix = rectangle.Transform.Value;
                                        matrix.Invert();
                                        Point newCurPoint = curPoint * matrix;
                                        rectangle.Rect = new Rect(rectangle.Rect.TopLeft, newCurPoint);
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        matrix.Invert();
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.Rotate:
                                    {
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Point topLeft = rectangle.Rect.TopLeft;
                                        Point topRight = rectangle.Rect.TopRight;
                                        Point bottomLeft = rectangle.Rect.BottomLeft;
                                        Point bottomRight = rectangle.Rect.BottomRight;
                                        Point center = new Point((topLeft.X + bottomRight.X) / 2, (topLeft.Y + bottomRight.Y) / 2);
                                        double angle = Math.Atan2(curPoint.Y - center.Y, curPoint.X - center.X) * 180.0 / Math.PI + 90.0;
                                        rectangle.Transform = new RotateTransform(angle, center.X, center.Y);
                                        Matrix matrix = rectangle.Transform.Value;
                                        Point newTopLeft = topLeft * matrix;
                                        Point newTopRight = topRight * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newTopLeft.X + newTopRight.X) / 2, (newTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.Internal:
                                    {
                                        double diffX = curPoint.X - this.downPoint.X;
                                        double diffY = curPoint.Y - this.downPoint.Y;
                                        RectangleGeometry rectangle = (RectangleGeometry)this.box.Data;
                                        Point newTopLeft = new Point(rectangle.Rect.TopLeft.X + diffX, rectangle.Rect.TopLeft.Y + diffY);
                                        Point newBottomRight = new Point(rectangle.Rect.BottomRight.X + diffX, rectangle.Rect.BottomRight.Y + diffY);
                                        Point center = new Point((newTopLeft.X + newBottomRight.X) / 2, (newTopLeft.Y + newBottomRight.Y) / 2);
                                        rectangle.Rect = new Rect(newTopLeft, newBottomRight);
                                        rectangle.Transform = new RotateTransform(((RotateTransform)rectangle.Transform).Angle, center.X, center.Y);
                                        this.downPoint = curPoint;
                                        Matrix matrix = rectangle.Transform.Value;
                                        Point topRight = rectangle.Rect.TopRight;
                                        Point newTopRight = topRight * matrix;
                                        Point newRTopLeft = newTopLeft * matrix;
                                        this.rotate.Data = new EllipseGeometry { RadiusX = 3, RadiusY = 3, Center = new Point((newRTopLeft.X + newTopRight.X) / 2, (newRTopLeft.Y + newTopRight.Y) / 2) };
                                    }
                                    break;
                                case EditType.None:
                                    break;
                            }
                            break;
                        }
                }
                UpdateEllipse();
            }
        }
    }
}